import pygame
import random, time, os
import redis

# 连接redis, 获取redis实例
key = 'score_rank'
r = redis.StrictRedis(host='localhost', port=6379, db=0)

# Constants常量
W, H = 288, 512
FPS = 30

# setUp设置
pygame.init()
# 设置屏幕大小
SCREEN = pygame.display.set_mode((W, H))
# 设置标题
pygame.display.set_caption('飞翔的小鸟---3组')
# 设置时钟
CLOCK = pygame.time.Clock()

# Materials 加载素材的区域
imagePath = 'assets/sprites'
IMAGES = {}
for image in os.listdir(imagePath):
    # 将所有的文件路径拆分成 文件名 + 后缀
    name, extension = os.path.splitext(image)
    path = os.path.join(imagePath, image)
    IMAGES[name] = pygame.image.load(path)

# 地面高度
FLOOR_Y = H - IMAGES['floor'].get_height()
# 所有音效
audioPath = 'assets/audio'
AUDIO = {}
for audio in os.listdir(audioPath):
    # 将所有的文件路径拆分成 文件名 + 后缀
    name, extension = os.path.splitext(audio)
    path = os.path.join(audioPath, audio)
    AUDIO[name] = pygame.mixer.Sound(path)


def main():
    # best_score = 0
    while True:
        # 播放音乐
        AUDIO['start'].play()
        # 随机一个背景，小鸟，水管的颜色
        IMAGES['bgpic'] = IMAGES[random.choice(['day', 'night'])]
        color = random.choice(['red', 'yellow', 'blue'])
        IMAGES['birds'] = [IMAGES[color + '-up'], IMAGES[color + '-mid'], IMAGES[color + '-down']]
        pipe = IMAGES[random.choice(['green-pipe', 'red-pipe'])]
        IMAGES['pipes'] = [pipe, pygame.transform.flip(pipe, False, True)]
        result = menu_window()
        # 死亡时接收小鸟的信息参数
        result = game_window(result)
        # 将score与存入redis中
        localtime = time.asctime(time.localtime(time.time()))
        r.zadd(key, {localtime: result['score']})
        # best_score = max(result['score'], best_score)
        # result['best_score'] = best_score
        end_window(result)


# 三个阶段
def menu_window():
    # 取排行榜前5个
    # print(r.zrevrange(key, 0, 4, withscores=True))

    # 地板长度
    floor_gap = IMAGES['floor'].get_width() - W
    floor_x = 0

    guide_x = (W - IMAGES['guide'].get_width()) / 2
    guide_y = (FLOOR_Y - IMAGES['guide'].get_height()) / 2

    bird_x = W * 0.2
    bird_y = (H - IMAGES['birds'][0].get_height()) / 2
    bird_y_vel = 1
    bird_y_range = [bird_y - 8, bird_y + 8]

    # 切换飞翔的小鸟图片
    idx = 0
    repeat = 1
    frames = [0] * repeat + [1] * repeat + [2] * repeat + [1] * repeat
    # 默认基本速度
    base_vel = -4
    max_base_vel = -6
    min_base_vel = -2

    while True:
        result = {'base_vel': base_vel}
        # 获取键盘事件
        for event in pygame.event.get():
            # 如果点击了退出
            if event.type == pygame.QUIT:
                quit()
            # 如果按下了空格键
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    return result
                if event.key == pygame.K_UP and base_vel > max_base_vel:
                    base_vel -= 1
                if event.key == pygame.K_DOWN and base_vel < min_base_vel:
                    base_vel += 1
                if event.key == pygame.K_c:
                    color = random.choice(['red', 'yellow', 'blue'])
                    IMAGES['birds'] = [IMAGES[color + '-up'], IMAGES[color + '-mid'], IMAGES[color + '-down']]
        floor_x += base_vel
        if floor_x <= - floor_gap:
            floor_x = 0

        # 小鸟上下移动
        bird_y += bird_y_vel
        if bird_y < bird_y_range[0] or bird_y > bird_y_range[1]:
            bird_y_vel *= -1

        idx += 1
        idx %= len(frames)

        SCREEN.blit(IMAGES['bgpic'], (0, 0))
        SCREEN.blit(IMAGES['floor'], (floor_x, FLOOR_Y))
        SCREEN.blit(IMAGES['guide'], (guide_x, guide_y))
        SCREEN.blit(IMAGES['birds'][frames[idx]], (bird_x, bird_y))
        showdifficulty(abs(base_vel))
        pygame.display.update()
        # 每秒10个画面
        CLOCK.tick(FPS)


def game_window(result):
    base_vel = result['base_vel']
    score = 0
    AUDIO['flap'].play()
    # 地板移动的距离
    floor_gap = IMAGES['floor'].get_width() - W
    floor_x = 0

    bird = Bird(W * 0.2, H * 0.4)

    distance = 200
    pipe_gap = 150
    n_pairs = 3
    pipe_group = pygame.sprite.Group()
    pipe_upgroup = pygame.sprite.Group()
    prop_group = pygame.sprite.Group()
    for i in range(n_pairs):
        pipe_y = random.randint(int(H * 0.3), int(H * 0.7))
        pipe_up = Pipe(W + i * distance, pipe_y, base_vel, True)
        pipe_down = Pipe(W + i * distance, pipe_y - pipe_gap, base_vel, False)
        pipe_group.add(pipe_up)
        pipe_group.add(pipe_down)
        pipe_upgroup.add(pipe_up)

    # 是否暂停
    pause = False
    while True:
        base_vel = result['base_vel']
        # pipe_gap = random.randint(85, 125)
        flap = False
        # 获取键盘事件
        for event in pygame.event.get():
            # 如果点击了退出
            if event.type == pygame.QUIT:
                quit()
            if event.type == pygame.KEYDOWN:
                # 如果按下了空格键
                if event.key == pygame.K_SPACE and not pause:
                    flap = True
                    AUDIO['flap'].play()
                if event.key == pygame.K_p:
                    pause = not pause
        if not pause:
            # 地图移动
            floor_x += base_vel
            if floor_x <= - floor_gap:
                floor_x = 0
            # 飞
            bird.update(flap)

            first_pipe_up = pipe_group.sprites()[0]
            first_pipe_down = pipe_group.sprites()[1]

            if first_pipe_up.rect.right < 0:
                pipe_y = random.randint(int(H * 0.3), int(H * 0.7))
                new_pipe_up = Pipe(first_pipe_up.rect.x + n_pairs * distance, pipe_y, base_vel, True)
                new_pipe_down = Pipe(first_pipe_up.rect.x + n_pairs * distance, pipe_y - pipe_gap, base_vel, False)
                pipe_group.add(new_pipe_up)
                pipe_group.add(new_pipe_down)
                pipe_upgroup.add(new_pipe_up)

                # 初始化变小道具
                if random.random() < 0.3:
                    t = random.random()
                    if t < 0.25:
                        diminish = DiminishProp(new_pipe_up.rect.x, new_pipe_up.rect.y - IMAGES['0'].get_rect().height - 4, base_vel, '0')
                        prop_group.add(diminish)
                    elif t < 0.5:
                        diminish = DiminishProp(new_pipe_down.rect.x, new_pipe_down.rect.bottom + 4, base_vel, '0')
                        prop_group.add(diminish)
                    elif t < 0.75:
                        slow = SlowProp(new_pipe_down.rect.x, new_pipe_down.rect.bottom + 4, base_vel, '1')
                        prop_group.add(slow)
                    else:
                        slow = SlowProp(new_pipe_up.rect.x, new_pipe_up.rect.y - IMAGES['0'].get_rect().height - 4, base_vel, '1')
                        prop_group.add(slow)

                first_pipe_up.kill()
                first_pipe_down.kill()

            pipe_group.update(base_vel)

            # 检测小鸟是否与精灵组里任意一个精灵有碰撞
            collided_sprite = pygame.sprite.spritecollideany(bird, pipe_group)

            # 判断小鸟是否死亡
            if bird.rect.y > FLOOR_Y or bird.rect.y < 0 or collided_sprite:
                AUDIO['hit'].play()
                AUDIO['die'].play()
                result = {'bird': bird, 'score': score}
                return result

            # 得分  小鸟每过一个管道得一分
            birdmidplace = bird.rect.x + bird.image.get_width() / 2  # 小鸟中间位置
            #  判断小鸟中线是否过管道中线
            for pipe in pipe_upgroup:
                pipemidplace = pipe.rect.x + pipe.image.get_width() / 2
                # 刚越过一个管道得该管道的一分
                if pipemidplace <= birdmidplace < pipemidplace - base_vel:
                    score += 1
                    AUDIO['score'].play()  # 得分音效

            # 检测小鸟是否与道具碰撞
            collided_sprite = pygame.sprite.spritecollideany(bird, prop_group)
            result['bird'] = bird
            if collided_sprite:
                collided_sprite.after(result)

            SCREEN.blit(IMAGES['bgpic'], (0, 0))
            # 绘制管道组
            pipe_group.draw(SCREEN)
            # 绘制得分
            showscore(score, 1)
            # 地板
            SCREEN.blit(IMAGES['floor'], (floor_x, FLOOR_Y))
            # 小鸟
            SCREEN.blit(bird.image, bird.rect)

            # 变小道具
            prop_group.draw(SCREEN)
            prop_group.update(base_vel)
            pygame.display.update()
            # 每秒10个画面
            CLOCK.tick(FPS)


def end_window(result):
    bird = result['bird']
    score = result['score']
    # best_score = result['best_score']
    best_score = int(r.zrevrange(key, 0, 0, withscores=True)[0][1])

    gameover_x = (W - IMAGES['gameover'].get_width()) / 2
    gameover_y = (FLOOR_Y - IMAGES['gameover'].get_height()) / 2
    while True:
        # 获取键盘事件
        for event in pygame.event.get():
            # 如果点击了退出
            if event.type == pygame.QUIT:
                quit()
            # 如果按下了空格键
            if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:
                return

        # 小鸟死亡时掉落
        bird.go_die()

        SCREEN.blit(IMAGES['bgpic'], (0, 0))
        showscore(score, 1)
        showscore(best_score, 2)
        SCREEN.blit(IMAGES['floor'], (0, FLOOR_Y))
        SCREEN.blit(IMAGES['gameover'], (gameover_x, gameover_y))
        SCREEN.blit(IMAGES['birds'][0], (W / 2, H * 0.3))
        SCREEN.blit(bird.image, bird.rect)
        pygame.display.update()
        # 每秒10个画面
        CLOCK.tick(FPS)


def showscore(score, position):  # int得分转字符列表转图片
    scorenumbers = [x for x in str(score)]  # 得分['1','2']->12
    scorewidth = 0  # 得分图片的总宽度
    #  遍历列表得到得分总宽度
    for number in scorenumbers:
        scorewidth += IMAGES[number].get_width()
    #  以总宽度与屏幕宽度确定得分图片位置使其在屏幕中央
    score_x = (W - scorewidth) / 2
    #  遍历列表显示图片
    for number in scorenumbers:
        SCREEN.blit(IMAGES[number], (score_x, H * 0.12 * position))
        score_x += IMAGES[number].get_width()



def showdifficulty(vel):  # 显示难度选择
    velnumbers = str(vel)  # 难度2-6
    velwidth = IMAGES[velnumbers].get_width()  # 难度数值图片宽度
    iconwidth = IMAGES['nandu'].get_width()   # 难度图标宽度
    score_x = (W - velwidth - iconwidth) / 2  # 起始位置
    SCREEN.blit(IMAGES['nandu'], (score_x, H * 0.24))  # 贴图
    SCREEN.blit(IMAGES[velnumbers], (score_x + iconwidth + 5, H * 0.24))

# 小鸟类
class Bird(pygame.sprite.Sprite):
    def __init__(self, x, y):
        pygame.sprite.Sprite.__init__(self)
        self.frames = [0] * 5 + [1] * 5 + [2] * 5 + [1] * 5
        self.idx = 0
        self.images = IMAGES['birds']
        self.image = self.images[self.frames[self.idx]]
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        # 上下移动
        self.y_vel = -10
        self.max_y_vel = 10
        self.gravity = 1
        # 旋转
        self.rotate = 45
        self.max_rotate = -30
        self.rotate_vel = -3
        # 如果按下空格，就初始化为跳跃的时候
        self.y_vel_after_flap = -10
        self.rotate_after_flap = 45

    def update(self, flap=False):

        if flap:
            self.y_vel = self.y_vel_after_flap
            self.rotate = self.rotate_after_flap

        self.y_vel = min(self.y_vel + self.gravity, self.max_y_vel)
        self.rect.y += self.y_vel
        self.rotate = max(self.rotate + self.rotate_vel, self.max_rotate)

        self.idx += 1
        self.idx %= len(self.frames)
        self.image = self.images[self.frames[self.idx]]
        self.image = pygame.transform.rotate(self.image, self.rotate)

    def go_die(self):
        if self.rect.y < FLOOR_Y:
            self.rect.y += self.max_y_vel
            self.rotate = -90
            self.image = self.images[self.frames[self.idx]]
            self.image = pygame.transform.rotate(self.image, self.rotate)


# 管道类
class Pipe(pygame.sprite.Sprite):
    def __init__(self, x, y, base_vel, upwards=True):
        pygame.sprite.Sprite.__init__(self)
        if upwards:
            self.image = IMAGES['pipes'][0]
            self.rect = self.image.get_rect()
            self.rect.x = x
            self.rect.top = y
        else:
            self.image = IMAGES['pipes'][1]
            self.rect = self.image.get_rect()
            self.rect.x = x
            self.rect.bottom = y
        self.x_vel = base_vel

    def update(self, base_vel):
        self.rect.x += base_vel


# 抽象道具类
class Prop(pygame.sprite.Sprite):
    def __init__(self, x, y, base_vel, name):
        pygame.sprite.Sprite.__init__(self)
        self.image = IMAGES[name]
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        self.x_vel = base_vel

    # 与小鸟碰撞后执行的函数
    def after(self, options):
        pass

    # 更新Prop
    def update(self, base_vel):
        pass


# 变小道具
class DiminishProp(Prop):
    def __init__(self, x, y, base_vel, name):
        Prop.__init__(self, x, y, base_vel, name)

    def after(self, result):
        bird = result['bird']
        proportion = 0.5
        rect = bird.rect
        i = 0
        for image in bird.images:
            image = pygame.transform.scale(image, (int(rect.width * proportion), int(rect.height * proportion)))
            bird.images[i] = image
            i += 1
        self.kill()

    def update(self, base_vel):
        self.rect.x += base_vel


# 管道移动速度变慢的道具
class SlowProp(Prop):
    def __init__(self, x, y, base_vel, name):
        Prop.__init__(self, x, y, base_vel, name)

    def after(self, result):
        if result['base_vel'] < -2:
            result['base_vel'] += 1
        self.kill()

    def update(self, base_vel):
        self.rect.x += base_vel


main()
